import { RuleTester } from "@typescript-eslint/rule-tester";
import { explicitTableIds } from "../lib/explicitTableIds.js";

const ruleTester = new RuleTester({
  languageOptions: {
    parserOptions: {
      ecmaVersion: 2020,
      sourceType: "module",
      projectService: {
        allowDefaultProject: ["*.ts*", "convex/*.ts*"],
      },
    },
  },
});

ruleTester.run("explicit-table-ids", explicitTableIds, {
  valid: [
    // Already migrated get call
    {
      code: `
        import { query } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const getMessage = query({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            return await ctx.db.get("messages", messageId);
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Already migrated replace call
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const updateMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.replace("messages", messageId, { text: "updated" });
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Already migrated patch call
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const patchMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.patch("messages", messageId, { text: "patched" });
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Already migrated delete call
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const deleteMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.delete("messages", messageId);
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Not a db call
    {
      code: `
        import { query } from "./_generated/server";

        export const list = query({
          args: {},
          handler: async (ctx, args) => {
            return await ctx.db.query("messages").collect();
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Regression test for a false positive in IntDate.get from jsrsasign
    {
      code: `
        declare namespace MyNamespace {
          function get(parameter: number): string;
        }

        async function _ignoreUnrelatedFunctionsFromNamespaces() {
          MyNamespace.get(1);
        }
      `,
      filename: "convex/messages.ts",
    },
    // Ignore methods from lib types
    {
      code: `
        new URL("https://www.convex.dev?test=1").searchParams.get("test");
      `,
      filename: "convex/messages.ts",
    },
    // Ignore .replace on string
    {
      code: `
        console.log("test".replace("test", "test2"));
      `,
      filename: "convex/messages.ts",
    },
  ],
  invalid: [
    // Unmigrated get call
    {
      code: `
        import { query } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const getMessage = query({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            return await ctx.db.get(messageId);
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import { query } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const getMessage = query({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            return await ctx.db.get("messages", messageId);
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Unmigrated replace call
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const updateMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.replace(messageId, { text: "updated" });
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const updateMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.replace("messages", messageId, { text: "updated" });
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Unmigrated patch call
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const patchMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.patch(messageId, { text: "patched" });
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const patchMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.patch("messages", messageId, { text: "patched" });
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Unmigrated delete call
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const deleteMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.delete(messageId);
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const deleteMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            await ctx.db.delete("messages", messageId);
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Multiple unmigrated calls in one function
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const updateMultiple = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            const userId: Id<"users"> = "456" as any;

            const message = await ctx.db.get(messageId);
            const user = await ctx.db.get(userId);

            await ctx.db.patch(messageId, { author: user?.name });
          },
        });
      `,
      errors: [
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
      ],
      output: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const updateMultiple = mutation({
          args: {},
          handler: async (ctx, args) => {
            const messageId: Id<"messages"> = "123" as any;
            const userId: Id<"users"> = "456" as any;

            const message = await ctx.db.get("messages", messageId);
            const user = await ctx.db.get("users", userId);

            await ctx.db.patch("messages", messageId, { author: user?.name });
          },
        });
      `,
      filename: "convex/messages.ts",
    },
    // Unmigrated call with different table name
    {
      code: `
        import { query } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const getUser = query({
          args: {},
          handler: async (ctx, args) => {
            const userId: Id<"users"> = "123" as any;
            return await ctx.db.get(userId);
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import { query } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const getUser = query({
          args: {},
          handler: async (ctx, args) => {
            const userId: Id<"users"> = "123" as any;
            return await ctx.db.get("users", userId);
          },
        });
      `,
      filename: "convex/users.ts",
    },
    // Type is `any` - should report error but no auto-fix
    {
      code: `
        import { query } from "./_generated/server";

        export const getMessage = query({
          args: {},
          handler: async (ctx, args) => {
            const id: any = "123";
            return await ctx.db.get(id);
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name-no-inference",
        },
      ],
      filename: "convex/messages.ts",
    },
    // Type is `Id<any>` - should report error but no auto-fix
    {
      code: `
        import { query } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const getMessage = query({
          args: {},
          handler: async (ctx, args) => {
            const id: Id<any> = "123" as Id<any>;
            return await ctx.db.get(id);
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name-no-inference",
        },
      ],
      filename: "convex/messages.ts",
    },
    // Type is `any` with db.patch - should report error but no auto-fix
    {
      code: `
        import { mutation } from "./_generated/server";

        export const updateMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const id: any = "123";
            await ctx.db.patch(id, { text: "updated" });
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name-no-inference",
        },
      ],
      filename: "convex/messages.ts",
    },
    // Type is `Id<any>` with db.delete - should report error but no auto-fix
    {
      code: `
        import { mutation } from "./_generated/server";
        import { Id } from "./_generated/dataModel";

        export const deleteMessage = mutation({
          args: {},
          handler: async (ctx, args) => {
            const id: Id<any> = "123" as Id<any>;
            await ctx.db.delete(id);
          },
        });
      `,
      errors: [
        {
          messageId: "missing-table-name-no-inference",
        },
      ],
      filename: "convex/messages.ts",
    },
    // DatabaseReader type - should detect and auto-fix
    {
      code: `
        import { Id } from "./_generated/dataModel";
        import { DatabaseReader } from "./_generated/server";

        async function fromDbReader(db: DatabaseReader, id: Id<"documents">) {
          await db.get(id);
        }
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import { Id } from "./_generated/dataModel";
        import { DatabaseReader } from "./_generated/server";

        async function fromDbReader(db: DatabaseReader, id: Id<"documents">) {
          await db.get("documents", id);
        }
      `,
      filename: "convex/helpers.ts",
    },
    // GenericDatabaseReader type - should detect and auto-fix
    {
      code: `
        import {
          GenericDatabaseReader,
          GenericDataModel,
        } from "convex/server";
        import { Id } from "./_generated/dataModel";

        async function fromGenericDbReader(
          db: GenericDatabaseReader<GenericDataModel>,
          id: Id<"documents">,
        ) {
          await db.get(id);
        }
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import {
          GenericDatabaseReader,
          GenericDataModel,
        } from "convex/server";
        import { Id } from "./_generated/dataModel";

        async function fromGenericDbReader(
          db: GenericDatabaseReader<GenericDataModel>,
          id: Id<"documents">,
        ) {
          await db.get("documents", id);
        }
      `,
      filename: "convex/helpers.ts",
    },
    // GenericDatabaseReader with extends - should detect and auto-fix
    {
      code: `
        import {
          GenericDatabaseReader,
          GenericDataModel,
        } from "convex/server";
        import { Id } from "./_generated/dataModel";

        async function fromGenericDbReaderExtends<
          SomeDataModel extends GenericDataModel,
        >(db: GenericDatabaseReader<SomeDataModel>, id: Id<"documents">) {
          await db.get(id);
        }
      `,
      errors: [
        {
          messageId: "missing-table-name",
        },
      ],
      output: `
        import {
          GenericDatabaseReader,
          GenericDataModel,
        } from "convex/server";
        import { Id } from "./_generated/dataModel";

        async function fromGenericDbReaderExtends<
          SomeDataModel extends GenericDataModel,
        >(db: GenericDatabaseReader<SomeDataModel>, id: Id<"documents">) {
          await db.get("documents", id);
        }
      `,
      filename: "convex/helpers.ts",
    },
    // DatabaseWriter type - should detect and auto-fix all methods
    {
      code: `
        import { Id } from "./_generated/dataModel";
        import { DatabaseWriter } from "./_generated/server";

        async function fromDbWriter(db: DatabaseWriter, id: Id<"documents">) {
          await db.get(id);
          await db.replace(id, { name: "test2" });
          await db.patch(id, { name: "test3" });
          await db.delete(id);
        }
      `,
      errors: [
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
      ],
      output: `
        import { Id } from "./_generated/dataModel";
        import { DatabaseWriter } from "./_generated/server";

        async function fromDbWriter(db: DatabaseWriter, id: Id<"documents">) {
          await db.get("documents", id);
          await db.replace("documents", id, { name: "test2" });
          await db.patch("documents", id, { name: "test3" });
          await db.delete("documents", id);
        }
      `,
      filename: "convex/helpers.ts",
    },
    // GenericDatabaseWriter type - should detect and auto-fix all methods
    {
      code: `
        import {
          GenericDatabaseWriter,
          GenericDataModel,
        } from "convex/server";
        import { Id } from "./_generated/dataModel";

        async function fromGenericDbWriter(
          db: GenericDatabaseWriter<GenericDataModel>,
          id: Id<"documents">,
        ) {
          await db.get(id);
          await db.replace(id, { name: "test2" });
          await db.patch(id, { name: "test3" });
          await db.delete(id);
        }
      `,
      errors: [
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
        { messageId: "missing-table-name" },
      ],
      output: `
        import {
          GenericDatabaseWriter,
          GenericDataModel,
        } from "convex/server";
        import { Id } from "./_generated/dataModel";

        async function fromGenericDbWriter(
          db: GenericDatabaseWriter<GenericDataModel>,
          id: Id<"documents">,
        ) {
          await db.get("documents", id);
          await db.replace("documents", id, { name: "test2" });
          await db.patch("documents", id, { name: "test3" });
          await db.delete("documents", id);
        }
      `,
      filename: "convex/helpers.ts",
    },
  ],
});
